//
//  GLESVertexBufferObject.m
//  VertexBufferObject
//
//  Created by 畑圭輔 on 10/03/28.
//  Copyright 2010 Keisuke Hata. All rights reserved.
//

#import "GLESVertexBufferObject.h"
#import "TextureUtils.h"

@implementation GLESTexturedVertexBufferObject

- (void) initVertexBufferObject {
	
	// VBO用バッファを作成、データを関連付け
	glGenBuffers(1, &_vboID);
	glBindBuffer(GL_ARRAY_BUFFER, _vboID);
	glBufferData(GL_ARRAY_BUFFER, sizeof( VBOTexturedPixel ) * _gridTotalNum , 
				 _pixels ,
				 GL_DYNAMIC_DRAW);
	
	// 元に戻しておく
	glBindBuffer(GL_ARRAY_BUFFER, 0);
}

- (void) initPixelBuffer {

	int x = 0 , y = 0;
	int pixelIndex = 0;
	
	NSAssert( _texture != nil , @"Texture is nil" );
	for (y = 0;y < _gridHeightNum; y++) {
		for (x = 0;x < _gridWidthNum;x++ ) {
			
			pixelIndex = x + y * _gridWidthNum;
			int colorIndex = 0;
			for (colorIndex = 0; colorIndex < 3*2; colorIndex++) {
				// カラー
				_pixels[ pixelIndex ].pixels[colorIndex].colors.red   = 1.0f;
				_pixels[ pixelIndex ].pixels[colorIndex].colors.green = 1.0f;
				_pixels[ pixelIndex ].pixels[colorIndex].colors.blue  = 1.0f;
				_pixels[ pixelIndex ].pixels[colorIndex].colors.alpha = 1.0f;
			}
			
			// UV
			VBOTexturedVertex *vertex = _pixels[ pixelIndex ].pixels;
			float margin_t = (float)_gridSize / (float)_texture.realwidth;
			float margin_s = (float)_gridSize / (float)_texture.realheight;
			vertex[0].uv.x = margin_t * x;
			vertex[0].uv.y = margin_s * y;
			vertex[1].uv.x = margin_t * x;
			vertex[1].uv.y = margin_s * y + margin_s;
			vertex[2].uv.x = margin_t * x + margin_t;
			vertex[2].uv.y = margin_s * y;
			vertex[3].uv.x = margin_t * x;
			vertex[3].uv.y = margin_s * y + margin_s;
			vertex[4].uv.x = margin_t * x + margin_t;
			vertex[4].uv.y = margin_s * y;
			vertex[5].uv.x = margin_t * x + margin_t;
			vertex[5].uv.y = margin_s * y + margin_s;

			// 位置
			vertex[0].pos.x = x * _gridSize;
			vertex[0].pos.y = y * _gridSize;

			vertex[1].pos.x = x * _gridSize;
			vertex[1].pos.y = y * _gridSize + _gridSize;

			vertex[2].pos.x = x * _gridSize + _gridSize;
			vertex[2].pos.y = y * _gridSize;

			vertex[3].pos.x = x * _gridSize;
			vertex[3].pos.y = y * _gridSize + _gridSize;

			vertex[4].pos.x = x * _gridSize + _gridSize;
			vertex[4].pos.y = y * _gridSize;

			vertex[5].pos.x = x * _gridSize + _gridSize;
			vertex[5].pos.y = y * _gridSize + _gridSize;
		}
	}
}

- (id) initWithGrid : (NSString *)path vboGridSize:(float)gridSize {
	
	_pixels =  0;
	_texture = nil;
	
	if( self = [super init] ) {
		
		// テクスチャ読み込み
		_texture = [[Texture2D alloc] initWithContentsOfFile:path];
		if( _texture == nil ) {
			[self release];
			return nil;
		}
		
		// グリッドサイズは、読み込むテクスチャのサイズに合わせたものを
		// 指定する事。w = 128の場合 10とか割り切れないものは指定しない。
		// 運用の都合があるのでアサート等は入れてません。
		_gridSize      = gridSize;
		_gridWidthNum  = _texture.realwidth  / gridSize;
		_gridHeightNum = _texture.realheight / gridSize;
		_gridTotalNum  = _gridWidthNum * _gridHeightNum;
		
		_pixels = (VBOTexturedPixel *)malloc( 
				 sizeof( VBOTexturedPixel ) * _gridTotalNum );
		bzero( _pixels, sizeof( VBOTexturedPixel ) * _gridTotalNum );
		
		// VBOデータを作成
		[self initVertexBufferObject];
		
		// ピクセルデータを初期化
		[self initPixelBuffer];
		
		// ピクセルデータを反映
		[self presentBuffer];
	}
	return self;
}

// VBO頂点データを一気に更新
- (void) presentBuffer {
	
	// 作成した頂点バッファをバインド
	glBindBuffer(GL_ARRAY_BUFFER, _vboID);
	// 更新した頂点データを転送
	glBufferSubData(GL_ARRAY_BUFFER, 0, 
					sizeof( VBOTexturedPixel )*_gridTotalNum, 
					_pixels );
	glBindBuffer(GL_ARRAY_BUFFER, 0);
}

- (void) draw {
	
	glEnable( GL_TEXTURE_2D );
	glEnable( GL_BLEND );
	glEnableClientState( GL_VERTEX_ARRAY );
	glEnableClientState( GL_COLOR_ARRAY);
	glEnableClientState( GL_TEXTURE_COORD_ARRAY );
	
	// TextureUtilsを使って、カレントのバインドテクスチャ番号を変更
	GLuint _blend_s = GL_SRC_ALPHA;
	GLuint _blend_d = GL_ONE_MINUS_SRC_ALPHA;
	[TextureUtils setTextureBlend:_blend_s blendDst:_blend_d];
	GLuint _magfilter = GL_NEAREST;
	GLuint _minfilter = GL_NEAREST;
	[TextureUtils setTextureMagFilter:_magfilter];
	[TextureUtils setTextureMinFilter:_minfilter];
	GLuint _wrap_t = GL_CLAMP_TO_EDGE;
	GLuint _wrap_s = GL_CLAMP_TO_EDGE;
	[TextureUtils setTextureWrapS:_wrap_s];
	[TextureUtils setTextureWrapT:_wrap_t];
	
	[TextureUtils bindTexture2D:_texture];
	
	glBindBuffer(GL_ARRAY_BUFFER, _vboID);
	
	// 頂点情報
	glVertexPointer(2,GL_FLOAT,sizeof(VBOTexturedVertex),0);
	
	// カラー情報
	glColorPointer(4, GL_FLOAT, sizeof(VBOTexturedVertex),(GLvoid*)offsetof(VBOTexturedVertex,colors) );

	// UV情報
	glTexCoordPointer(2, GL_FLOAT, sizeof(VBOTexturedVertex), (GLvoid*)offsetof(VBOTexturedVertex,uv) );

	// 現在有効なパーティクルの数を描画
	// drawまでにインクリメントされた _activeParticleIndex = 描画数
	glDrawArrays(GL_TRIANGLES, 0, _gridTotalNum*3*2);
	
	// 元に戻す
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	
	glDisableClientState( GL_TEXTURE_COORD_ARRAY );
	glDisableClientState( GL_COLOR_ARRAY);
	glDisableClientState( GL_VERTEX_ARRAY );
	glDisable( GL_BLEND );
	glDisable( GL_TEXTURE_2D );
}

- (void) dealloc {
	
	// VBO用バッファを削除
	glDeleteBuffers(1, &_vboID);
	
	if( _pixels )
		free( _pixels );
	
	[super dealloc];
}

@end
